//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

import { makeAutoObservable, runInAction } from 'mobx';
import type { NodeInfo, EdgeInfo, EdgeKey } from "@/api/roadmap_content";
import { list_node, list_edge, get_node, get_edge, remove_node, remove_edge } from "@/api/roadmap_content";
import { request } from '@/utils/request';
import type { ReactFlowInstance } from 'reactflow';
import type { StateInfo } from "@/api/roadmap_state";
import { list_state } from "@/api/roadmap_state";
import type { RoadmapInfo } from '@/api/roadmap';
import { get as get_roadmap } from '@/api/roadmap';
import { get as get_admin_roadmap } from '@/api/roadmap_admin';
import type { NoteInfo } from "@/api/roadmap_user";
import { list_note } from "@/api/roadmap_user";

export interface MousePosition {
    x: number;
    y: number;
}

export class RoadmapStore {
    constructor() {
        makeAutoObservable(this);
    }

    private _roadmapId = "";
    private _adminUser = false;
    private _canUpdate = false;
    private _sessionId = "";
    private _inEdit = false;
    private _showNote = false;
    private _lockSizeAndPosition = false;

    get roadmapId() {
        return this._roadmapId;
    }

    get adminUser() {
        return this._adminUser;
    }

    get canUpdate() {
        return this._canUpdate;
    }

    get sessionId() {
        return this._sessionId;
    }

    get inEdit() {
        return this._inEdit;
    }

    set inEdit(val: boolean) {
        runInAction(() => {
            this._inEdit = val;
            if (val == false) {
                this.loadStateList();
                this._mousePosition = null;
            }
        });
    }

    get showNote() {
        return this._showNote;
    }

    set showNote(val: boolean) {
        runInAction(() => {
            this._showNote = val;
        });
    }

    get lockSizeAndPosition() {
        return this._lockSizeAndPosition;
    }

    set lockSizeAndPosition(val: boolean) {
        runInAction(() => {
            this._lockSizeAndPosition = val;
        });
    }

    init(roadmapId: string, sessionId: string, adminUser: boolean, canUpdate: boolean, showNote: boolean) {
        runInAction(() => {
            this._roadmapId = roadmapId;
            this._sessionId = sessionId;
            this._adminUser = adminUser;
            this._canUpdate = canUpdate;
            this._inEdit = adminUser && canUpdate;
            this._showNote = showNote;
        });
    }

    private _roadmapInfo: RoadmapInfo | null = null;
    private _nodeList: NodeInfo[] = [];
    private _edgeList: EdgeInfo[] = [];

    get roadmapInfo() {
        return this._roadmapInfo;
    }

    get nodeList() {
        return this._nodeList;
    }

    set nodeList(val: NodeInfo[]) {
        runInAction(() => {
            this._nodeList = val;
        });
    }

    getNode(nodeId: string): NodeInfo | undefined {
        return this._nodeList.find(item => item.node_id == nodeId);
    }

    get edgeList() {
        return this._edgeList;
    }

    getEdge(edgeKey: EdgeKey): EdgeInfo | undefined {
        return this._edgeList.find(item => item.edge_key.from_node_id == edgeKey.from_node_id &&
            item.edge_key.from_handle_id == edgeKey.from_handle_id &&
            item.edge_key.to_node_id == edgeKey.to_node_id &&
            item.edge_key.to_handle_id == edgeKey.to_handle_id);
    }

    async loadRoadmapInfo() {
        if (this._adminUser) {
            const res = await request(get_admin_roadmap({
                session_id: this._sessionId,
                roadmap_id: this._roadmapId,
            }));

            runInAction(() => {
                this._roadmapInfo = res.roadmap;
            });
        } else {
            const res = await request(get_roadmap({
                session_id: this._sessionId,
                roadmap_id: this._roadmapId,
            }));

            runInAction(() => {
                this._roadmapInfo = res.roadmap;
            });
        }

    }

    async loadNodeList() {
        const res = await request(list_node({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
        }));
        runInAction(() => {
            this._nodeList = res.node_list;
        });
    }

    async loadEdgeList() {
        const res = await request(list_edge({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
        }));
        runInAction(() => {
            this._edgeList = res.edge_list;
        });
    }

    onUpdateNodeSize(nodeId: string, w: number, h: number) {
        const tmpList = this._nodeList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index != -1) {
            tmpList[index].basic_info.node_size = { w, h };
            runInAction(() => {
                this._nodeList = tmpList;
            });
        }
    }

    onUpdateNodePosition(nodeId: string, x: number, y: number) {
        const tmpList = this._nodeList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index != -1) {
            tmpList[index].basic_info.node_position = { x, y };
            runInAction(() => {
                this._nodeList = tmpList;
            });
        }
    }

    async onUpdateNode(nodeId: string) {
        const tmpList = this._nodeList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        const res = await request(get_node({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            node_id: nodeId,
        }));
        if (index != -1) {
            tmpList[index] = res.node;
        } else {
            tmpList.push(res.node);
        }

        runInAction(() => {
            this._nodeList = tmpList;
        });
    }

    async onUpdateEdge(edgeKey: EdgeKey) {
        const tmpList = this._edgeList.slice();
        const index = tmpList.findIndex(item => item.edge_key.from_node_id == edgeKey.from_node_id &&
            item.edge_key.from_handle_id == edgeKey.from_handle_id &&
            item.edge_key.to_node_id == edgeKey.to_node_id &&
            item.edge_key.to_handle_id == edgeKey.to_handle_id);

        const res = await request(get_edge({
            session_id: this._sessionId,
            edge_key: edgeKey,
        }));
        if (index != -1) {
            tmpList[index] = res.edge;
        } else {
            tmpList.push(res.edge);
        }

        runInAction(() => {
            this._edgeList = tmpList;
        });
    }

    onRemoveNode(nodeId: string) {
        const tmpNodeList = this._nodeList.filter(item => item.node_id != nodeId);
        const tmpEdgeList = this._edgeList.filter(item => item.edge_key.from_node_id != nodeId && item.edge_key.to_node_id != nodeId);

        runInAction(() => {
            this._nodeList = tmpNodeList;
            this._edgeList = tmpEdgeList;
        });
    }

    onRemoveEdge(edgeKey: EdgeKey) {
        const tmpList = this._edgeList.filter(item => !(item.edge_key.from_node_id == edgeKey.from_node_id &&
            item.edge_key.from_handle_id == edgeKey.from_handle_id &&
            item.edge_key.to_node_id == edgeKey.to_node_id &&
            item.edge_key.to_handle_id == edgeKey.to_handle_id));

        runInAction(() => {
            this._edgeList = tmpList;
        });
    }

    private _flowInstance: ReactFlowInstance | null = null;

    get flowInstance(): ReactFlowInstance | null {
        return this._flowInstance;
    }

    set flowInstance(val: ReactFlowInstance) {
        runInAction(() => {
            this._flowInstance = val;
        });
    }

    private _selectNodeId = "";
    private _selectEdgeKey: EdgeKey | null = null;

    get selectNodeId() {
        return this._selectNodeId;
    }

    set selectNodeId(val: string) {
        runInAction(() => {
            this._selectNodeId = val;
            if (val != "") {
                this._selectEdgeKey = null;
            }
        });
    }

    get selectEdgeKey() {
        return this._selectEdgeKey;
    }

    set selectEdgeKey(val: EdgeKey | null) {
        runInAction(() => {
            this._selectEdgeKey = val;
            if (val != null) {
                this._selectNodeId = "";
            }
        });
    }

    //用户状态
    private _stateList: StateInfo[] = [];

    get stateList() {
        return this._stateList;
    }

    async loadStateList() {
        if (this._adminUser) {
            return;
        }
        const res = await request(list_state({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            filterby_node_id: false,
            node_id: "",
        }));
        runInAction(() => {
            this._stateList = res.state_list;
        });
    }

    async onUpdateNodeState(nodeId: string) {
        const res = await request(list_state({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            filterby_node_id: true,
            node_id: nodeId,
        }));
        let tmpList = this._stateList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index == -1) {
            tmpList.push(...res.state_list);
        } else {
            if (res.state_list.length == 0) {
                tmpList = tmpList.filter(item => item.node_id != nodeId);
            } else if (res.state_list.length == 1) {
                tmpList[index] = res.state_list[0];
            }
        }

        runInAction(() => {
            this._stateList = tmpList;
        });
    }

    //用户学习笔记
    private _noteList: NoteInfo[] = [];

    get noteList() {
        return this._noteList;
    }

    async loadNoteList() {
        if (this._adminUser) {
            return;
        }
        const res = await request(list_note({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            filter_by_node_id: false,
            node_id: "",
        }));
        runInAction(() => {
            this._noteList = res.note_list;
        });
    }

    async onUpdateNote(nodeId: string) {
        const res = await request(list_note({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            filter_by_node_id: true,
            node_id: nodeId,
        }));
        let tmpList = this._noteList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index == -1) {
            tmpList.push(...res.note_list);
        } else {
            if (res.note_list.length == 0) {
                tmpList = tmpList.filter(item => item.node_id != nodeId);
            } else {
                tmpList[index] = res.note_list[0];
            }
        }

        runInAction(() => {
            this._noteList = tmpList;
        });
    }

    //批量操作节点
    private _batchNodeIdList: string[] = [];

    get batchNodeIdList() {
        return this._batchNodeIdList;
    }

    addBatchNodeId(nodeId: string) {
        const tmpList = this._batchNodeIdList.slice();
        if (tmpList.includes(nodeId)) {
            return;
        }
        tmpList.push(nodeId);
        runInAction(() => {
            this._batchNodeIdList = tmpList;
        });
    }

    removeBatchNodeId(nodeId: string) {
        const tmpList = this._batchNodeIdList.filter(item => item != nodeId);
        runInAction(() => {
            this._batchNodeIdList = tmpList;
        });
    }

    clearBatchNodeIdList() {
        runInAction(() => {
            this._batchNodeIdList = [];
        });
    }

    //删除节点和连接
    async removeNode(nodeId: string) {
        await request(remove_node({
            session_id: this._sessionId,
            roadmap_id: this._roadmapId,
            node_id: nodeId,
        }));
        this.selectNodeId = "";
        this.removeBatchNodeId(nodeId);
        this.onRemoveNode(nodeId);
    }

    async removeEdge(edgeKey: EdgeKey) {
        await request(remove_edge({
            session_id: this._sessionId,
            edge_key: edgeKey,
        }));
        this.selectEdgeKey = null;
        this.onRemoveEdge(edgeKey);
    }

    //鼠标节点为主
    private _mousePosition: MousePosition | null = null;

    get mousePosition() {
        return this._mousePosition;
    }

    set mousePosition(val: MousePosition | null) {
        runInAction(() => {
            this._mousePosition = val;
        });
    }
}